/* ========================================================================= */
/* ------------------------------------------------------------------------- */
/*!
  \file			PageMdl.cc
  \date			July 2011
  \author		TNick

  \brief		Contains the implementation for PageMdl class


 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @n
 Please read ReadMe.txt and Licence.txt in root folder @n
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @n

*/
/* ------------------------------------------------------------------------- */
/* ========================================================================= */
//
//
//
//
/*  INCLUDES    ------------------------------------------------------------ */

class       MainWindow;

#include	<QDebug>

#include	<filters/abstractfilter.h>
//#include    <filters/flt_regexnc.h>

#include	"pagemdl.h"

extern      MainWindow * g_MW;


/*  INCLUDES    ============================================================ */
//
//
//
//
/*  CLASS    --------------------------------------------------------------- */

/* ------------------------------------------------------------------------- */
PageMdl::PageMdl	(QObject *parent) :
	QAbstractItemModel(parent),
	page_mutex(QMutex::Recursive)
{
	dmd = ALL;
	filt_in_cnt = 0;
	filt_out_cnt =  0;

	/* the filters; as new filters are added, this is the place where should
   be inserted in the "system"; custom filters amy be added here, too,
   by implementing a plug-in system (trivial but unnecessary) */
    //Flt_RegExNC::initFilter();
    p_in_filter = NULL;
	p_out_filter = NULL;

}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
PageMdl::~PageMdl	(void)
{

}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void            PageMdl::appendNewPage           (PageData * p_new)
{
	bool pg_ps = AbstractFilter::pagePasses(p_new);

	/* append this in chain */
	LOCK_MUTEX_LOCK;
	pages_header.prepend(p_new);
	if (pg_ps)
		{
			appendFilterIn(NULL, p_new);
		}
	else
		{
			appendFilterOut(NULL, p_new);
		}
	MUTEX_UNLOCK_MUTEX;

}
/* ========================================================================= */


/* ------------------------------------------------------------------------- */
QModelIndex		PageMdl::index(int a_row, int a_column,
							   const QModelIndex &parent) const
{

	QMutexLocker locker((QMutex*)&page_mutex);
	Q_UNUSED(locker);

	if ((a_row < 0) || (a_column < 0))
		{
			qDebug() << "  weird location";
			return QModelIndex();
		}

	/* diferentiate between root and leaf items */
	if (parent.isValid())
		{	/* this is a leaf */
			return QModelIndex();
		}

	/* this is a top level item */

	//LOCK_MUTEX_LOCK;
	PageData*	it;
	switch  (dmd)        {
	case    (FILTERED): {
        it = ((PageMdl*)this)->nthFilterIn(a_row);
        break;
    }
	case    (UNFILTERED): {
        it = ((PageMdl*)this)->nthFilterOut(a_row);
        break;
    }
	default:
		it = ((PageMdl*)this)->item(a_row);
		break;

	}

	//MUTEX_UNLOCK_MUTEX;

	if (it == NULL)
		return QModelIndex();
	return createIndex(a_row, a_column, it);

}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
int				PageMdl::rowCount	(const QModelIndex & parent) const
{
	Q_UNUSED(parent);
	int i;

	LOCK_MUTEX_LOCK;
	switch  (dmd)        {
	case    (FILTERED): {
        i = filt_in_cnt;
        break;}
	case    (UNFILTERED): {
        i = filt_out_cnt;
        break;}
	default:
		i = pages_header.count();
		break;}
	MUTEX_UNLOCK_MUTEX;

	return i;
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
QVariant		PageMdl::data		(
	const QModelIndex & midx, int role ) const
{

	QVariant var;

	if (role != Qt::DisplayRole)
		return QVariant();
	if (!midx.isValid())
		return QVariant();

	LOCK_MUTEX_LOCK;
	PageData*	it = static_cast<PageData *>(midx.internalPointer());
	if (it == NULL)
		goto EmptyExit;

	switch  (midx.column())        {
	case    0:  {
        var = QVariant(it->displName());
        break;}
	case    1:  {
        var = QVariant(it->URL());
        break;}
	case    2:  {
        var = QVariant(it->dateOfRetrieval());
        break;}
	}

    EmptyExit:
        MUTEX_UNLOCK_MUTEX;

	return var;
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
QVariant		PageMdl::headerData		(
	int section, Qt::Orientation orientation, int role) const
{
	Q_UNUSED(orientation);

	if (role != Qt::DisplayRole)
		return QVariant();

	if (orientation == Qt::Horizontal)
		{
			if (section > 2)
				return QVariant();

			switch  (section)        {
			case    0:  {
                return QVariant(tr("Name"));
            }
			case    1:  {
                return QVariant(tr("Location"));
            }
			case    2:  {
                return QVariant(tr("Retrieved"));
            }
			}
		}
	else
		{
			return QVariant(section+1);
		}
	return QVariant();

}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
Qt::ItemFlags	PageMdl::flags					(
	const QModelIndex &		index) const
{
	Q_UNUSED(index);
	if (!index.isValid())
		return Qt::NoItemFlags;
	if (index.column() == 0)
		return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
	else
		return Qt::ItemIsEnabled;
}
/* ========================================================================= */


/* ------------------------------------------------------------------------- */
void            PageMdl::setDisplayMode     (DisplMode new_mode)
{
	/* ignore silly requests */
	if (dmd == new_mode)
		return;

	QAbstractItemModel::beginResetModel();
	if (
			(filterInChain() == NULL) &&
			(filterOutChain() == NULL) &&
			(count() > 0))
		{
			createChains();
		}
	dmd = new_mode;
	QAbstractItemModel::endResetModel();
}
/* ========================================================================= */


/* ------------------------------------------------------------------------- */
/// adds a page to the end of the filtered in chain
void            PageMdl::appendFilterIn              (
	PageData * p_prev, PageData * p_new)
{
	p_new->setNextFltPage(NULL);
	filt_in_cnt++;
	if (p_prev == NULL)
		{
			if (p_in_filter == NULL)
				{
					p_in_filter = p_new;
					return;
				}
			else
				{
					p_prev = p_in_filter;
					while (p_prev->nextFltPage() != NULL)
						{
							p_prev = p_prev->nextFltPage();
						}
				}
		}
	p_prev->setNextFltPage(p_new);
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
/// adds a page to the end of the filtered out chain
void            PageMdl::appendFilterOut              (
	PageData * p_prev, PageData * p_new)
{
	p_new->setNextFltPage(NULL);
	filt_out_cnt++;
	if (p_prev == NULL)
		{
			if (p_out_filter == NULL)
				{
					p_out_filter = p_new;
					return;
				}
			else
				{
					p_prev = p_out_filter;
					while (p_prev->nextFltPage() != NULL)
						{
							p_prev = p_prev->nextFltPage();
						}
				}
		}
	p_prev->setNextFltPage(p_new);
}
/* ========================================================================= */


/* ------------------------------------------------------------------------- */
void        PageMdl::createChains    (void)
{
	PageData * p_itr = first();
	PageData * hint_in = NULL;
	PageData * hint_out = NULL;

	LOCK_MUTEX_LOCK;

    if (displayMode()!=ALL)
        {
            beginResetModel();
        }
    resetChains();
	while (p_itr != NULL)
		{
			if (AbstractFilter::pagePasses(p_itr))
				{
					appendFilterIn(hint_in, p_itr);
					hint_in = p_itr;
				}
			else
				{
					appendFilterOut(hint_out, p_itr);
					hint_out = p_itr;
				}

			p_itr = p_itr->next();
		}
	MUTEX_UNLOCK_MUTEX;
    if (displayMode()!=ALL)
        {
            endResetModel();
        }
}
/* ========================================================================= */


/* ------------------------------------------------------------------------- */
PageData *      PageMdl::nthFilterIn         (int i)
{
	/*
 Root item, as stored in the class, is the last item. To get an item i we
 actually need to find (n-i-1) item in the chain
*/
	LOCK_MUTEX_LOCK;
	if (i >= filt_in_cnt)
		{
			MUTEX_UNLOCK_MUTEX;
			return NULL;
		}
	PageData * p_itr = p_in_filter;
	i = filt_in_cnt - i - 1;
	while (p_itr != NULL)
		{
			if (i == 0)
				break;
			p_itr = p_itr->nextFltPage();
			i--;
		}
	MUTEX_UNLOCK_MUTEX;
	return p_itr;
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
PageData *      PageMdl::nthFilterOut        (int i)
{
	/*
 Root item, as stored in the class, is the last item. To get an item i we
 actually need to find (n-i-1) item in the chain
*/
	LOCK_MUTEX_LOCK;
	if (i >= filt_out_cnt)
		{
			MUTEX_UNLOCK_MUTEX;
			return NULL;
		}
	PageData * p_itr = p_out_filter;
	i = filt_out_cnt - i - 1;
	while (p_itr != NULL)
		{
			if (i == 0)
				break;
			p_itr = p_itr->nextFltPage();
			i--;
		}
	MUTEX_UNLOCK_MUTEX;
	return p_itr;
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void            PageMdl::deletePage          (PageData* p_to_del)
{

    if (p_to_del == NULL)
        return;
    int idx = itemIndex(p_to_del);
    if (idx == -1)
        {
            qDebug() << "Attempt to remove an item that is not part of this model";
            return;
        }

    beginRemoveRows(QModelIndex(),idx,idx);

    /* first, remove item from master chain */
    remove(p_to_del);


    PageData* p_prev = p_in_filter;
    if (p_in_filter == p_prev)
        {
            p_in_filter = p_to_del->nextFltPage();
            filt_in_cnt--;
            goto ItemFoundInChains;
        }
    while (p_prev->nextFltPage() != NULL)
        {
            if (p_prev->nextFltPage() == p_to_del)
                {
                    p_prev->setNextFltPage(p_to_del->nextFltPage());
                    filt_in_cnt--;
                    goto ItemFoundInChains;
                }
            p_prev = p_prev->nextFltPage();
        }

    p_prev = p_out_filter;
    if (p_out_filter == p_prev)
        {
            p_out_filter = p_to_del->nextFltPage();
            filt_in_cnt--;
            goto ItemFoundInChains;
        }
    while (p_prev->nextFltPage() != NULL)
        {
            if (p_prev->nextFltPage() == p_to_del)
                {
                    p_prev->setNextFltPage(p_to_del->nextFltPage());
                    filt_out_cnt--;
                    goto ItemFoundInChains;
                }
            p_prev = p_prev->nextFltPage();
        }

    ItemFoundInChains:
        delete p_to_del;
    endRemoveRows();
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void            PageMdl::deleteFiltIn        (void)
{
    if (p_in_filter == NULL)
        return;

    if (dmd != UNFILTERED)
        {
            beginResetModel();
        }

    PageData* p_prev;
    while (p_in_filter != NULL)
        {
            p_prev = p_in_filter->nextFltPage();
            remove(p_in_filter);
            delete p_in_filter;
            p_in_filter = p_prev;
        }

    p_in_filter = NULL;
    filt_in_cnt = 0;
    if (dmd != UNFILTERED)
        {
            endResetModel();
        }

}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void            PageMdl::deleteFiltOut       (void)
{
    if (filt_out_cnt == 0)
        return;

    if (dmd != FILTERED)
        {
            beginResetModel();
        }

    PageData* p_prev;
    while (p_out_filter != NULL)
        {
            p_prev = p_out_filter->nextFltPage();
            remove(p_out_filter);
            delete p_out_filter;
            p_out_filter = p_prev;
        }

    p_out_filter = NULL;
    filt_out_cnt = 0;
    if (dmd != UNFILTERED)
        {
            endResetModel();
        }

}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void            PageMdl::deleteAll           (void)
{

    beginResetModel();
    PageData* p_prev;
    PageData* p_1 = first();
    while (p_1 != NULL)
        {
            p_prev = p_1->nextFltPage();
            delete p_1;
            p_1 = p_prev;
        }
    setFirst(NULL);
    setCount(0);
    resetChains();
    endResetModel();
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void            PageMdl::deleteVisible       (void)
{
    switch (dmd)    {
    case FILTERED:  {
        deleteFiltIn();
        break;}
    case UNFILTERED:  {
        deleteFiltOut();
        break;}
    default:  {
        deleteAll();
        break;}
    }
}
/* ========================================================================= */


/*  CLASS    =============================================================== */
//
//
//
//
/* ------------------------------------------------------------------------- */
/* ========================================================================= */

